
module frv_iu_alu (
    input                       clk             ,
    input                       rst_n           ,
    input                       pd_rst          ,
    // ALU Control
    input                       alu_req         ,
    input [5:0]     alu_inst_id     ,
    input                       alu_ctrl_land   ,
    input                       alu_ctrl_lor    ,
    input                       alu_ctrl_lxor   ,
    input                       alu_ctrl_sll    ,
    input                       alu_ctrl_srl    ,
    input                       alu_ctrl_sra    ,
    input                       alu_ctrl_add    ,
    input                       alu_ctrl_sub    ,
    input                       alu_ctrl_slt    ,
    input                       alu_ctrl_unsign ,              
    // ALU operation 
    input [31:0]                alu_op1_val     ,
    input [31:0]                alu_op2_val     ,
    // the result of ALU will commit to the ROB ,and bypass to other FU concurrently
    output                      alu_resp_vld    ,
    output  [5:0]   alu_resp_inst_id,   
    output  [31:0]              alu_resp_rd_val     
);
 
wire [31:0] bm;
wire [31:0] aout;//alg out
wire [31:0] lout;//logic out
wire [31:0] sout,ashift;//shift out

wire [3:1]  logic_sel;//logic operation

wire sel_logic,sel_shift,sel_adder;//res op

wire cout,ov,neg;
wire ne,eq;//not equal and equal
wire slt_one;//set less than
wire lt,ge;//less than   great equal

wire                     res_vld;
wire [5:0]   res_inst_id;
wire [31:0]              res_rd_val;

assign bm[31:0] = ( alu_ctrl_sub ) ? ~alu_op2_val[31:0] : alu_op2_val[31:0];

assign {cout, aout[31:0]} = {1'b0, alu_op1_val[31:0]} + {1'b0, bm[31:0]} + {32'b0, alu_ctrl_sub};

assign ov = (~alu_op1_val[31] & ~bm[31] &  aout[31]) |
            ( alu_op1_val[31] &  bm[31] & ~aout[31] );

assign neg = aout[31];

assign eq = alu_op1_val[31:0] == alu_op2_val[31:0];

assign ne = ~eq;

assign logic_sel[3] = alu_ctrl_land | alu_ctrl_lor;
assign logic_sel[2] = alu_ctrl_lor  | alu_ctrl_lxor;
assign logic_sel[1] = alu_ctrl_lor  | alu_ctrl_lxor;

assign lout[31:0] = (  alu_op1_val[31:0] &  alu_op2_val[31:0] & {32{logic_sel[3]}} ) |
					(  alu_op1_val[31:0] & ~alu_op2_val[31:0] & {32{logic_sel[2]}} ) |
					( ~alu_op1_val[31:0] &  alu_op2_val[31:0] & {32{logic_sel[1]}} );

assign ashift[31:0] = alu_op1_val >>> alu_op2_val[4:0];

assign sout[31:0] = ( {32{alu_ctrl_sll}} & (alu_op1_val[31:0] <<  alu_op2_val[4:0]) ) |
					( {32{alu_ctrl_srl}} & (alu_op1_val[31:0] >>  alu_op2_val[4:0]) ) |
					( {32{alu_ctrl_sra}} &  ashift[31:0]        );

assign sel_logic = |{alu_ctrl_land,alu_ctrl_lor,alu_ctrl_lxor};

assign sel_shift = |{alu_ctrl_sll,alu_ctrl_srl,alu_ctrl_sra};

assign sel_adder = (alu_ctrl_add | alu_ctrl_sub) & ~alu_ctrl_slt;

assign lt = (~alu_ctrl_unsign & (neg ^ ov)) |
			( alu_ctrl_unsign & ~cout);

assign ge = ~lt;

assign slt_one = (alu_ctrl_slt & lt);

wire [31:0] alu_res;

assign alu_res[31:0] = ({32{sel_logic}} & lout[31:0]) |
						({32{sel_shift}} & sout[31:0]) |
						({32{sel_adder }} & aout[31:0]) |
						({31'b0, slt_one});                  

assign res_vld     = alu_req    ;
assign res_inst_id = alu_inst_id;
assign res_rd_val  = alu_res    ;

dffr #(1)               alu_res_vld_ff     (clk,rst_n,1'b1,res_vld     ,alu_resp_vld    );
dffr #(5+1) alu_res_inst_id_ff (clk,rst_n,1'b1,res_inst_id ,alu_resp_inst_id);
dffr #(32)              alu_res_rd_val_ff  (clk,rst_n,1'b1,res_rd_val  ,alu_resp_rd_val ); 


endmodule

